import { clamp } from "./util.js";
import RNG from "./rng.js";

export type Color = [number, number, number];

export function	fromString(str: string): Color {
	let cached:Color, r;
	if (str in CACHE) {
		cached = CACHE[str];
	} else {
		if (str.charAt(0) == "#") { // hex rgb

			let matched = str.match(/[0-9a-f]/gi) || [];
			let values = matched.map((x:string) => parseInt(x, 16));
			if (values.length == 3) {
				cached = values.map((x:number) => x*17) as Color;
			} else {
				for (let i=0;i<3;i++) {
					values[i+1] += 16*values[i];
					values.splice(i, 1);
				}
				cached = values as Color;
			}

		} else if ((r = str.match(/rgb\(([0-9, ]+)\)/i))) { // decimal rgb
			cached = r[1].split(/\s*,\s*/).map((x:string) => parseInt(x)) as Color;
		} else { // html name
			cached = [0, 0, 0];
		}

		CACHE[str] = cached;
	}

	return cached.slice() as Color;
}

/**
 * Add two or more colors
 */
export function add(color1: Color, ...colors: Color[]): Color {
	let result = color1.slice() as Color;
	for (let i=0;i<3;i++) {
		for (let j=0;j<colors.length;j++) {
			result[i] += colors[j][i];
		}
	}
	return result;
}

/**
 * Add two or more colors, MODIFIES FIRST ARGUMENT
 */
export function add_(color1: Color, ...colors: Color[]): Color {
	for (let i=0;i<3;i++) {
		for (let j=0;j<colors.length;j++) {
			color1[i] += colors[j][i];
		}
	}
	return color1;
}

/**
 * Multiply (mix) two or more colors
 */
export function multiply(color1: Color, ...colors: Color[]): Color {
	let result = color1.slice() as Color;
	for (let i=0;i<3;i++) {
		for (let j=0;j<colors.length;j++) {
			result[i] *= colors[j][i] / 255;
		}
		result[i] = Math.round(result[i]);
	}
	return result;
}

/**
 * Multiply (mix) two or more colors, MODIFIES FIRST ARGUMENT
 */
export function multiply_(color1: Color, ...colors: Color[]): Color {
	for (let i=0;i<3;i++) {
		for (let j=0;j<colors.length;j++) {
			color1[i] *= colors[j][i] / 255;
		}
		color1[i] = Math.round(color1[i]);
	}
	return color1;
}

/**
 * Interpolate (blend) two colors with a given factor
 */
export function interpolate(color1: Color, color2: Color, factor = 0.5): Color {
	let result = color1.slice() as Color;
	for (let i=0;i<3;i++) {
		result[i] = Math.round(result[i] + factor*(color2[i]-color1[i]));
	}
	return result;
}
export const lerp = interpolate;

/**
 * Interpolate (blend) two colors with a given factor in HSL mode
 */
export function interpolateHSL(color1: Color, color2: Color, factor = 0.5): Color {
	let hsl1 = rgb2hsl(color1);
	let hsl2 = rgb2hsl(color2);
	for (let i=0;i<3;i++) {
		hsl1[i] += factor*(hsl2[i]-hsl1[i]);
	}
	return hsl2rgb(hsl1);
}
export const lerpHSL = interpolateHSL;

/**
 * Create a new random color based on this one
 * @param color
 * @param diff Set of standard deviations
 */
export function randomize(color: Color, diff: number | Color): Color {
	if (!(diff instanceof Array)) { diff = Math.round(RNG.getNormal(0, diff)); }
	let result = color.slice() as Color;
	for (let i=0;i<3;i++) {
		result[i] += (diff instanceof Array ? Math.round(RNG.getNormal(0, diff[i])) : diff);
	}
	return result;
}

/**
 * Converts an RGB color value to HSL. Expects 0..255 inputs, produces 0..1 outputs.
 */
export function rgb2hsl(color: Color): Color {
	let r = color[0]/255;
	let g = color[1]/255;
	let b = color[2]/255;

	let max = Math.max(r, g, b), min = Math.min(r, g, b);
	let h = 0, s, l = (max + min) / 2;

	if (max == min) {
		s = 0; // achromatic
	} else {
		let d = max - min;
		s = (l > 0.5 ? d / (2 - max - min) : d / (max + min));
		switch (max) {
			case r: h = (g - b) / d + (g < b ? 6 : 0); break;
			case g: h = (b - r) / d + 2; break;
			case b: h = (r - g) / d + 4; break;
		}
		h /= 6;
	}

	return [h, s, l];
}

function hue2rgb(p: number, q: number, t: number) {
	if (t < 0) t += 1;
	if (t > 1) t -= 1;
	if (t < 1/6) return p + (q - p) * 6 * t;
	if (t < 1/2) return q;
	if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
	return p;
}

/**
 * Converts an HSL color value to RGB. Expects 0..1 inputs, produces 0..255 outputs.
 */
export function hsl2rgb(color: Color): Color {
	let l = color[2];

	if (color[1] == 0) {
		l = Math.round(l*255);
		return [l, l, l];
	} else {
		let s = color[1];
		let q = (l < 0.5 ? l * (1 + s) : l + s - l * s);
		let p = 2 * l - q;
		let r = hue2rgb(p, q, color[0] + 1/3);
		let g = hue2rgb(p, q, color[0]);
		let b = hue2rgb(p, q, color[0] - 1/3);
		return [Math.round(r*255), Math.round(g*255), Math.round(b*255)];
	}
}

export function toRGB(color: Color) {
	let clamped = color.map(x => clamp(x, 0, 255));
	return `rgb(${clamped.join(",")})`;
}

export function toHex(color: Color) {
	let clamped = color.map(x => clamp(x, 0, 255).toString(16).padStart(2, "0"));
	return `#${clamped.join("")}`;
}

const CACHE: Record<string, Color> = {
	"black": [0,0,0],
	"navy": [0,0,128],
	"darkblue": [0,0,139],
	"mediumblue": [0,0,205],
	"blue": [0,0,255],
	"darkgreen": [0,100,0],
	"green": [0,128,0],
	"teal": [0,128,128],
	"darkcyan": [0,139,139],
	"deepskyblue": [0,191,255],
	"darkturquoise": [0,206,209],
	"mediumspringgreen": [0,250,154],
	"lime": [0,255,0],
	"springgreen": [0,255,127],
	"aqua": [0,255,255],
	"cyan": [0,255,255],
	"midnightblue": [25,25,112],
	"dodgerblue": [30,144,255],
	"forestgreen": [34,139,34],
	"seagreen": [46,139,87],
	"darkslategray": [47,79,79],
	"darkslategrey": [47,79,79],
	"limegreen": [50,205,50],
	"mediumseagreen": [60,179,113],
	"turquoise": [64,224,208],
	"royalblue": [65,105,225],
	"steelblue": [70,130,180],
	"darkslateblue": [72,61,139],
	"mediumturquoise": [72,209,204],
	"indigo": [75,0,130],
	"darkolivegreen": [85,107,47],
	"cadetblue": [95,158,160],
	"cornflowerblue": [100,149,237],
	"mediumaquamarine": [102,205,170],
	"dimgray": [105,105,105],
	"dimgrey": [105,105,105],
	"slateblue": [106,90,205],
	"olivedrab": [107,142,35],
	"slategray": [112,128,144],
	"slategrey": [112,128,144],
	"lightslategray": [119,136,153],
	"lightslategrey": [119,136,153],
	"mediumslateblue": [123,104,238],
	"lawngreen": [124,252,0],
	"chartreuse": [127,255,0],
	"aquamarine": [127,255,212],
	"maroon": [128,0,0],
	"purple": [128,0,128],
	"olive": [128,128,0],
	"gray": [128,128,128],
	"grey": [128,128,128],
	"skyblue": [135,206,235],
	"lightskyblue": [135,206,250],
	"blueviolet": [138,43,226],
	"darkred": [139,0,0],
	"darkmagenta": [139,0,139],
	"saddlebrown": [139,69,19],
	"darkseagreen": [143,188,143],
	"lightgreen": [144,238,144],
	"mediumpurple": [147,112,216],
	"darkviolet": [148,0,211],
	"palegreen": [152,251,152],
	"darkorchid": [153,50,204],
	"yellowgreen": [154,205,50],
	"sienna": [160,82,45],
	"brown": [165,42,42],
	"darkgray": [169,169,169],
	"darkgrey": [169,169,169],
	"lightblue": [173,216,230],
	"greenyellow": [173,255,47],
	"paleturquoise": [175,238,238],
	"lightsteelblue": [176,196,222],
	"powderblue": [176,224,230],
	"firebrick": [178,34,34],
	"darkgoldenrod": [184,134,11],
	"mediumorchid": [186,85,211],
	"rosybrown": [188,143,143],
	"darkkhaki": [189,183,107],
	"silver": [192,192,192],
	"mediumvioletred": [199,21,133],
	"indianred": [205,92,92],
	"peru": [205,133,63],
	"chocolate": [210,105,30],
	"tan": [210,180,140],
	"lightgray": [211,211,211],
	"lightgrey": [211,211,211],
	"palevioletred": [216,112,147],
	"thistle": [216,191,216],
	"orchid": [218,112,214],
	"goldenrod": [218,165,32],
	"crimson": [220,20,60],
	"gainsboro": [220,220,220],
	"plum": [221,160,221],
	"burlywood": [222,184,135],
	"lightcyan": [224,255,255],
	"lavender": [230,230,250],
	"darksalmon": [233,150,122],
	"violet": [238,130,238],
	"palegoldenrod": [238,232,170],
	"lightcoral": [240,128,128],
	"khaki": [240,230,140],
	"aliceblue": [240,248,255],
	"honeydew": [240,255,240],
	"azure": [240,255,255],
	"sandybrown": [244,164,96],
	"wheat": [245,222,179],
	"beige": [245,245,220],
	"whitesmoke": [245,245,245],
	"mintcream": [245,255,250],
	"ghostwhite": [248,248,255],
	"salmon": [250,128,114],
	"antiquewhite": [250,235,215],
	"linen": [250,240,230],
	"lightgoldenrodyellow": [250,250,210],
	"oldlace": [253,245,230],
	"red": [255,0,0],
	"fuchsia": [255,0,255],
	"magenta": [255,0,255],
	"deeppink": [255,20,147],
	"orangered": [255,69,0],
	"tomato": [255,99,71],
	"hotpink": [255,105,180],
	"coral": [255,127,80],
	"darkorange": [255,140,0],
	"lightsalmon": [255,160,122],
	"orange": [255,165,0],
	"lightpink": [255,182,193],
	"pink": [255,192,203],
	"gold": [255,215,0],
	"peachpuff": [255,218,185],
	"navajowhite": [255,222,173],
	"moccasin": [255,228,181],
	"bisque": [255,228,196],
	"mistyrose": [255,228,225],
	"blanchedalmond": [255,235,205],
	"papayawhip": [255,239,213],
	"lavenderblush": [255,240,245],
	"seashell": [255,245,238],
	"cornsilk": [255,248,220],
	"lemonchiffon": [255,250,205],
	"floralwhite": [255,250,240],
	"snow": [255,250,250],
	"yellow": [255,255,0],
	"lightyellow": [255,255,224],
	"ivory": [255,255,240],
	"white": [255,255,255]
}
